//
// ssl/impl/host_name_verification.ipp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

//#include <boost/asio/detail/config.hpp>

#include <cctype>
#include <cstring>
#include <boost/asio/ip/address.hpp>
#include "host_name_verification.h"
#include <boost/asio/ssl/detail/openssl_types.hpp>

#include <boost/asio/detail/push_options.hpp>

namespace boost {
namespace asio {
namespace ssl {

bool host_name_verification::operator()(
    bool preverified, verify_context& ctx) const
{
  using namespace std; // For memcmp.

    //下面这一段我给注释掉，不然ssl握手将失败，暂不知道为何?
//  // Don't bother looking at certificates that have failed pre-verification.
//  if (!preverified)
//    return false;

  // We're only interested in checking the certificate at the end of the chain.
  // 我们只对检查链末端的证书感兴趣
  int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
  if (depth > 0)
    return true;

  // Try converting the host name to an address. If it is an address then we
  // need to look for an IP address in the certificate rather than a host name.
  // 尝试将主机名转换为地址。如果是地址，那么我们需要在证书中查找IP地址，而不是主机名
  boost::system::error_code ec;
  ip::address address = ip::make_address(host_, ec);
  const bool is_address = !ec;
  (void)address;

  X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());

  //uint32_t flags = X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT;

  if (is_address)
  {
    return X509_check_ip_asc(cert, host_.c_str(), 0) == 1;
    //return X509_check_ip_asc(cert, host_.c_str(), flags) == 1;
  }
  else
  {
    char* peername = 0;
    const int result = X509_check_host(cert,
        host_.c_str(), host_.size(), 0, &peername);

//    const int result = X509_check_host(cert,
//                                       host_.c_str(), host_.size(), flags, &peername);

    OPENSSL_free(peername);
    return result == 1;
  }
}

} // namespace ssl
} // namespace asio
} // namespace boost


/***
 * 下面是之前的验证的代码
 * **/

//bool mpAsioHttpClientSsl::match_pattern(const char* pattern,
//    std::size_t pattern_length, const char* host)
//{
//  const char* p = pattern;
//  const char* p_end = p + pattern_length;
//  const char* h = host;

//  while (p != p_end && *h)
//  {
//    if (*p == '*')
//    {
//      ++p;
//      while (*h && *h != '.')
//        if (match_pattern(p, p_end - p, h++))
//          return true;
//    }
//    else if (tolower(*p) == tolower(*h))
//    {
//      ++p;
//      ++h;
//    }
//    else
//    {
//      return false;
//    }
//  }

//  return p == p_end && !*h;
//}

//bool mpAsioHttpClientSsl::verify_certificate(bool preverified,
//                                             boost::asio::ssl::verify_context& ctx)
//{
//    // 我们只检查链末端的证书
//    int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle());
//    if (depth > 0)
//      return true;

//    boost::system::error_code ec;
//    boost::asio::ip::address address = boost::asio::ip::make_address(this->m_hostName, ec);
//    bool is_address = !ec;

//    X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
//    GENERAL_NAMES* gens = static_cast<GENERAL_NAMES*>(
//          X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0));

//    for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i)
//    {
//      GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i);
//      if (gen->type == GEN_DNS && !is_address)
//      {
//        ASN1_IA5STRING* domain = gen->d.dNSName;
//        if (domain->type == V_ASN1_IA5STRING && domain->data && domain->length)
//        {
//          const char* pattern = reinterpret_cast<const char*>(domain->data);
//          std::size_t pattern_length = domain->length;
//          if (match_pattern(pattern, pattern_length, this->m_hostName.c_str()))
//          {
//            GENERAL_NAMES_free(gens);
//            return true;
//          }
//        }
//      }
//      else if (gen->type == GEN_IPADD && is_address)
//      {
//        ASN1_OCTET_STRING* ip_address = gen->d.iPAddress;
//        if (ip_address->type == V_ASN1_OCTET_STRING && ip_address->data)
//        {
//          if (address.is_v4() && ip_address->length == 4)
//          {
//            boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes();
//            if (memcmp(bytes.data(), ip_address->data, 4) == 0)
//            {
//              GENERAL_NAMES_free(gens);
//              return true;
//            }
//          }
//          else if (address.is_v6() && ip_address->length == 16)
//          {
//            boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes();
//            if (memcmp(bytes.data(), ip_address->data, 16) == 0)
//            {
//              GENERAL_NAMES_free(gens);
//              return true;
//            }
//          }
//        }
//      }
//    }

//    GENERAL_NAMES_free(gens);

//    X509_NAME* name = X509_get_subject_name(cert);
//    int i = -1;
//    ASN1_STRING* common_name = 0;
//    while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
//    {
//      X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i);
//      common_name = X509_NAME_ENTRY_get_data(name_entry);
//    }

//    if (common_name && common_name->data && common_name->length)
//    {
//      const char* pattern = reinterpret_cast<const char*>(common_name->data);
//      std::size_t pattern_length = common_name->length;
//      if (match_pattern(pattern, pattern_length, this->m_hostName.c_str()))
//        return true;
//    }

//    char subject_name[256];
//    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
//    MPLOG_ERROR << "Verify Certificate fail , HOST NAME = " << m_hostName << "; SUBJECT NAME = " << subject_name;

//    return false;
////    char subject_name[256];
////    X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
////    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
////    std::cout << "Verifying " << subject_name << "\n";

////    return true;
//}

